<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="apple-touch-icon" href="apple-touch-icon.png">
    <link rel="shortcut icon" href="favicon.ico" />
    <link rel="manifest" href="a3.webmanifest">
    <title>A3 Template</title>
  </head>
  <body>
    <link rel="stylesheet" href="css/main.css"/>
    <script src="js/Theme.js"></script>
    <script src="js/ammo.js/build/ammo.wasm.js"></script>
    <div class="div-fullscreen"><div id="container"></div></div>
    <script type="module">
      import * as THREE from "./js/three.js/build/three.module.js";
      import Stats from './jsm/libs/stats.module.js';
		  import { OrbitControls } from './jsm/controls/OrbitControls.js';
      import { GUI } from './jsm/libs/dat.gui.module.js';
      
      // Graphics variables
      let container, stats;
      let camera, controls, scene, renderer;
      let ambientLight;

      let textureLoader;
      const clock = new THREE.Clock();

      // Physics variables
      const gravityConstant = - 9.8;
      let collisionConfiguration;
      let dispatcher;
      let broadphase;
      let solver;
      let softBodySolver;
      let physicsWorld;
      const rigidBodies = [];
      const margin = 0.05;
      let transformAux1;

      // gui
      let gui;
      
      let params = {
        // 背景
        sceneBackGroundColor: 0x1982cc,
        sceneLight:0x000000,
        cameraFov:60,
        cameraFar:2000,
        cameraNear:0.2,
        // 仿真
        simSpeed:1,
        simSpeeds:[0.1, 0.2, 0.5, 1, 2, 5],
        simStart:true,
        simPause:false,
        simReset:false,
      }


      Ammo().then( function ( AmmoLib ) {

        Ammo = AmmoLib;

        init();
        animate();

      } );


      function init() {

        initGraphics();

        initPhysics();

        createObjects();

        initInput();

        setupGUI();

      }

      function reset(){

      }

      
      function initGraphics() {

        container = document.getElementById( 'container' );
        
        camera = new THREE.PerspectiveCamera(
          params.cameraFov, window.innerWidth / window.innerHeight, 
          params.cameraNear, params.cameraFar
        );

        scene = new THREE.Scene();
        scene.background = new THREE.Color( params.sceneBackGroundColor );

        camera.position.set( - 4, 2, 4 );

        renderer = new THREE.WebGLRenderer({ alpha: true });
        renderer.setClearColor(0XFFFFFF, 0);
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.shadowMap.enabled = true;
        container.appendChild( renderer.domElement );

        controls = new OrbitControls( camera, renderer.domElement );
        controls.target.set( 0, 2, 0 );
        controls.update();

        textureLoader = new THREE.TextureLoader();

        ambientLight = new THREE.AmbientLight( params.sceneLight );
        scene.add( ambientLight );

        const light = new THREE.DirectionalLight( 0xffffff, 1 );
        light.position.set( 0, 10, 0 );
        light.castShadow = true;
        const d = 10;
        light.shadow.camera.left = - d;
        light.shadow.camera.right = d;
        light.shadow.camera.top = d;
        light.shadow.camera.bottom = - d;

        light.shadow.camera.near = 2;
        light.shadow.camera.far = 50;

        light.shadow.mapSize.x = 1024;
        light.shadow.mapSize.y = 1024;

        scene.add( light );

        stats = new Stats();
        stats.domElement.style.position = 'absolute';
        stats.domElement.style.top = '0px';
        container.appendChild( stats.domElement );

        //

        window.addEventListener( 'resize', onWindowResize );

      }

      function initPhysics() {

        // Physics configuration

        collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration();
        dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration );
        broadphase = new Ammo.btDbvtBroadphase();
        solver = new Ammo.btSequentialImpulseConstraintSolver();
        softBodySolver = new Ammo.btDefaultSoftBodySolver();
        physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver );
        physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );
        physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );

        transformAux1 = new Ammo.btTransform();

      }

      function createObjects() {
        const pos = new THREE.Vector3();
        const quat = new THREE.Quaternion();

        // Ground
        pos.set( 0, - 0.5, 0 );
        quat.set( 0, 0, 0, 1 );
        const ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) );
        ground.castShadow = true;
        ground.receiveShadow = true;
        textureLoader.load( "textures/grid.png", function ( texture ) {

          texture.wrapS = THREE.RepeatWrapping;
          texture.wrapT = THREE.RepeatWrapping;
          texture.repeat.set( 40, 40 );
          ground.material.map = texture;
          ground.material.needsUpdate = true;

        } );
      }

      function initInput() {

        window.addEventListener( 'keydown', function ( event ) {


        } );

        window.addEventListener( 'keyup', function () {

        } );

      }

      function setupGUI(){
        
        gui = new GUI();
        // 场景
        const guiScene = gui.addFolder('场景');
        guiScene.addColor(params, 'sceneBackGroundColor').name('背景色').onChange((val)=>{
          scene.background.set(val);
        });
        guiScene.addColor(params, 'sceneLight').name('环境光').onChange((val)=>{
          ambientLight.color.set(val);
        });
        guiScene.add(params, 'cameraFov', 1, 360, 1).name('相机视角').onChange((val)=>{
          camera.fov = val;
          camera.updateProjectionMatrix();
        });
        guiScene.add(params, 'cameraFar', 10, 9999, 1).name('相机远景').onChange((val)=>{
          camera.far = val;
          camera.updateProjectionMatrix();
        });
        guiScene.add(params, 'cameraNear', 0.01, 10, 0.01).name('相机近景').onChange((val)=>{
          camera.near = val;
          camera.updateProjectionMatrix();
        });

        // 仿真
        const guiSimulation = gui.addFolder('仿真');
        const guiSimStart = guiSimulation.add(params, 'simStart').name('开始').onChange((val)=>{
          if(val){
            guiSimStart.name('开始');
            guiSimReset.setValue(false);
          }
          else{
            guiSimStart.name('停止');
          }
        });
        const guiSimPause = guiSimulation.add(params, 'simPause').name('暂停').onChange((val)=>{
          if(!val){
            guiSimPause.name('暂停');
          }
          else{
            guiSimPause.name('继续');
          }
        });
        const guiSimReset = guiSimulation.add(params, 'simReset').name('重置').onChange((val)=>{
          if(val){
            reset();
            guiSimStart.setValue(true);
            guiSimPause.setValue(false);
          }
        });
        guiSimulation.add(params, 'simSpeed').name('仿真速度').options(params.simSpeeds);
      }

      function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize( window.innerWidth, window.innerHeight );

      }

      function createParalellepiped( sx, sy, sz, mass, pos, quat, material ) {

      const threeObject = new THREE.Mesh( new THREE.BoxGeometry( sx, sy, sz, 1, 1, 1 ), material );
      const shape = new Ammo.btBoxShape( new Ammo.btVector3( sx * 0.5, sy * 0.5, sz * 0.5 ) );
      shape.setMargin( margin );

      createRigidBody( threeObject, shape, mass, pos, quat );

      return threeObject;

      }

      function createRigidBody( threeObject, physicsShape, mass, pos, quat ) {

        threeObject.position.copy( pos );
        threeObject.quaternion.copy( quat );

        const transform = new Ammo.btTransform();
        transform.setIdentity();
        transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
        transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
        const motionState = new Ammo.btDefaultMotionState( transform );

        const localInertia = new Ammo.btVector3( 0, 0, 0 );
        physicsShape.calculateLocalInertia( mass, localInertia );

        const rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
        const body = new Ammo.btRigidBody( rbInfo );

        threeObject.userData.physicsBody = body;

        scene.add( threeObject );

        if ( mass > 0 ) {

          rigidBodies.push( threeObject );

          // Disable deactivation
          body.setActivationState( 4 );

        }

        physicsWorld.addRigidBody( body );

        }

      function animate() {

        requestAnimationFrame( animate );

        render();
        stats.update();

      }

      function render() {

        const deltaTime = clock.getDelta() / params.simSpeed;
        updatePhysics( deltaTime )
        
        renderer.setViewport( 0, 0,  window.innerWidth, window.innerHeight );
        renderer.render( scene, camera ); 
      }

      function updatePhysics( deltaTime ) {

        // Step world
        physicsWorld.stepSimulation( deltaTime );

        // Update rigid bodies
        for ( let i = 0, il = rigidBodies.length; i < il; i ++ ) {

          const objThree = rigidBodies[ i ];
          const objPhys = objThree.userData.physicsBody;
          const ms = objPhys.getMotionState();
          if ( ms ) {

            ms.getWorldTransform( transformAux1 );
            const p = transformAux1.getOrigin();
            const q = transformAux1.getRotation();
            objThree.position.set( p.x(), p.y(), p.z() );
            objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );

          }
        }
        
      }
      
    </script>
  </body>
</html>